#pragma once

#include "Socket.hpp"
#include <sys/select.h>

using namespace socket_ns;
using namespace log_ns;

class SelectServer
{
    static const int gnum = sizeof(fd_set) * 8;
    static const int gdefault = -1;

public:
    SelectServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
    }

    void Init()
    {
        for (int i = 0; i < gnum; ++i)
        {
            select_fd[i] = gdefault;
        }
        select_fd[0] = _listensock->Sockfd();
    }

    void Acceptor()
    {
        InetAddr cliaddr;
        int sockfd = _listensock->Accepter(&cliaddr);
        if (sockfd > 0)
        {
            LOG(DEBUG, "get a new link, client info %s\n", cliaddr.AddrStr().c_str());

            bool flag = false;
            for(int pos = 0 ; pos < gnum ; ++pos)
            {
                if(select_fd[pos] == gdefault)
                {
                    select_fd[pos] = sockfd;
                    flag = true;
                    break;
                }
            }

            if(!flag)
            {
                //说明select以达到上限，不在再等在多余的套接字，释放连接。
                LOG(ERROR,"select have be full");
                ::close(sockfd);
            }
        }
        else
        {
            LOG(ERROR, "Accept error %s\n");
            return;
        }
    }

    void Handler(int i)
    {
        char buff[1024];
        int n = ::recv(select_fd[i], buff, sizeof(buff) - 1, 0);
        if (n > 0)
        {
            buff[n] = 0;
            std::cout << "client say# " << buff << std::endl;
            std::string content = "<html><body><h1>hello bite</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            // echo_str += buffer;
            ::send(select_fd[i], echo_str.c_str(), echo_str.size(), 0); // 临时方案
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit\n");
            ::close(select_fd[i]);
            select_fd[i] = gdefault;
        }
        else
        {
            LOG(ERROR, "Read Error\n");
            ::close(select_fd[i]);
            select_fd[i] = gdefault;
        }
    }

    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < gnum; ++i)
        {
            if (select_fd[i] == gdefault)
                continue;
            // 收到事件
            if (FD_ISSET(select_fd[i], &rfds))
            {
                // 监听套接字 收到事件
                if (_listensock->Sockfd() == select_fd[i])
                {
                    Acceptor();
                }
                // 普通套接字 收到事件
                else
                {
                    Handler(i);
                }
            }
        }
    }

    void Loop()
    {
        while (true)
        {
            // 1. 创建文件描述符集合
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd = gdefault;

            // 2. 合法的fd添加至rfds文件描述符集合 && 更新出一个监听的文件描述符中值最大的
            for (int i = 0; i < gnum; ++i)
            {
                if (select_fd[i] != gnum)
                {
                    FD_SET(select_fd[i], &rfds);
                    if(maxfd < select_fd[i])
                        maxfd = select_fd[i];
                }
            }
            // FD_SET(_listensock->Sockfd(),&rfds);

            struct timeval timeout = {3, 0};
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);

            switch (n)
            {
            case 0:
                LOG(INFO, "time out, %d.%d\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                LOG(ERROR, "select error\n");
                break;
            default:
                LOG(DEBUG, "had event ready, n = %d\n", n);
                HandlerEvent(rfds);
                PrintDebug();
                break;
            }
        }
    }
    void PrintDebug()
    {
        std::cout << "fd list: ";
        for (int i = 0; i < gnum; i++)
        {
            if (select_fd[i] == gdefault)
                continue;
            std::cout << select_fd[i] << " ";
        }
        std::cout << "\n";
    }

    ~SelectServer()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;

    int select_fd[gnum];
};